package edu.bw.utils;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Component
public class TransactionProxyFactory {

    @Autowired
    private TransactionManager transactionManager;

    @Autowired
    private ApplicationContext applicationContext;//注入spring容器

    /**
     * @param cls 传递被代理的类
     * @param
     * @return 例如:传递ProductServiceImpl.class,将来该方法就返回ProductServiceImpl代理对象
     */

    @SuppressWarnings("unchecked")  //压制警告 防止部分代码背景变黄色
    public Object createProxyInstance(Class cls) {
        if (cls.getInterfaces().length != 0) {  //判断传递过来的被代理类cls是否实现了接口,如果实现了接口我们就使用JDK动态代理
            return Proxy.newProxyInstance(
                    cls.getClassLoader(),
                    cls.getInterfaces(),
                    /**
                     * proxy:底层生成的代理类对象
                     * method:通过代理类对象调用的方法凤凰在那个成的method对象
                     * args:通过代理类对象调用的方法传递的参数
                     */
                    ((proxy, method, args) -> invokeWithTransaction(cls,method,args))
            );
        } else {//当前被代理类没有实现任何父接口,此时使用CGLIB实现动态代理,将被代理类作为父类
            //1.利用Enhancer增强被代理类,通过它生成代理类对象
            Enhancer enhancer = new Enhancer();
            //2.设置将来生成的代理类的父类
            enhancer.setSuperclass(cls);
            //3.设置回调函数,当通过代理类对象调用方法的时候,转调回调函数
            enhancer.setCallback(
                    /**
                     * obj:CGlib生成的代理类对象
                     * method:通过代理类对象调用方法,封装的Method对象
                     * args:通过代理类对象调用方法时传递的参数
                     * proxy:方法的代理对象
                     */
                    (MethodInterceptor)(obj,method,args,proxy)->invokeWithTransaction(cls,method,args)
            );
            //4.创建代理类对象并返回
            return enhancer.create();
        }
    }


    /**
     *
     * @param cls 被代理类
     * @param method 将来通过被代理对象调用的方法封装成的method
     * @param args 将来通过代理对象调用的方法传递的参数
     * @return 通过代理对象的方法的返回值
     */
    private Object invokeWithTransaction(Class cls, Method method, Object[] args) {
        Object retVal = null;//存储最终方法的返回值

        if (method.getName().startsWith("find")) {//如果该条件为true,说明通过代理类对象调用的方法以find开头的查询相关方法
            try {
                retVal = method.invoke(applicationContext.getBean(cls), args);//相当于ProductServiceImpl对象调用find开头的方法
            }catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            //执行else说明通过代理类对象调用是增删改(update,delete,insert)相关的方法我们才加入事务控制
            try {
                transactionManager.beginTransaction();
                retVal = method.invoke(applicationContext.getBean(cls), args);//相当于通过ProductServiceImpl对象调用增删改方法
//                            int i=1/0;
                transactionManager.commit();
            } catch (Exception e) {
                transactionManager.rollback();
                throw new RuntimeException(e);
            } finally {
                transactionManager.release();
            }
        }
        return retVal;
    }
}
























